home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ldb.zip / LDB.DOC < prev    next >
Text File  |  1991-10-21  |  19KB  |  372 lines

  1.  
  2.  
  3. Dear C++ Programmer,
  4.  
  5.  
  6. Thank you for downloading the Loose Data Binder (LDB).  The LDB is
  7. a generic persistent container class that is less filling (smaller
  8. code sizes and faster execution) and less stuffy (no towering 
  9. convoluted hierarchies) than other conventional container class 
  10. libraries found packaged with C++ compilers and application 
  11. framework tools.
  12.  
  13. The LDB is offered to you as shareware, meaning try before you buy.
  14. For other than evaluation purposes directed at reaching a buy or 
  15. no buy decision, you are required by law to register the LDB.  A 
  16. hard copy manual will be sent to users upon receiving registration.
  17. The source code is also provided on either 3.5" or 5 1/4" DOS 
  18. diskette (please specify).  The source is broken down into many 
  19. files along with a makefile for library building.  PSW reserves 
  20. the right to withdraw this offer at any time.
  21.  
  22.     LDB v1.4  $30 in U.S.  $40 elsewhere
  23.     
  24.     Please make checks payable to:
  25.  
  26.         PSW / Power SoftWare
  27.         P.O. Box 10072
  28.         McLean, VA 22102 8072 USA
  29.  
  30. If you have questions about the LDB you can contact me at:
  31.  
  32.         John Small
  33.         Voice: (703) 759-3838
  34.         CIS: 73757,2233
  35.         Exec PC:  MSMALL
  36.         
  37. I have included tutorial chapter from the manual to get you
  38. started along with seven demos.
  39.  
  40.  
  41.                             Chapter 3
  42.  
  43.                             Tutorial
  44.  
  45.  
  46. Copy binder.hpp, sbinder.hpp, sdata.hpp, and cbinder.hpp to your
  47. compiler's standard header directory and the *.cpp files to your
  48. source code directory if you have not already done so.  Refer to
  49. the header file listings in the appendix for a quick reference to
  50. the various member functions of the LDB.  If a member function's
  51. operation is unclear, look up its entry in the reference chapter
  52. for an explanation.  Be sure to study the code of these examples,
  53. then compile (don't forget to link to binder.obj, sbinder.obj,
  54. sdata.obj and/or cbinder.obj as appropriate) and run them to see
  55. the results.  Are the results what you expected?  Okay let's begin. 
  56.  
  57.  
  58.                            BDRDEM1.CPP
  59.  
  60. This first example is a demo of the two Binder class constructors
  61. used in various configurations.  Find their declarations in
  62. binder.hpp or the reference chapter and make a mental note of their
  63. parameters and defaults!  I will only highlight some of their uses
  64. here.
  65.  
  66. Okay let's walk through the code of this first example which can of
  67. course be found in bdrdem1.cpp.  Starting in main() the first
  68. Binder, B1, is defined and constructed with all default parameters. 
  69. As you can see from binder.hpp, the binder is constructed with
  70. flags = BDR_NO_FREE, maxNodes = BDR_MAXNODES, limit = BDR_LIMIT,
  71. and delta = BDR_DELTA.  Don't worry about this stuff for now.  All
  72. we have here is a very basic binder behaving as an elastic array of
  73. void pointers which you supply.
  74.  
  75.      (see bdrdem1.cpp)  omitted here to reduce download size
  76.  
  77. The for loop inserts the char pointers of the vector V into the
  78. rear of the binder treating it as if it were a queue.  The loop is
  79. terminated when the NULL pointer of V is attempted to be inserted
  80. into the binder queue.  The insQ() primitive always returns the
  81. pointer it inserts into the binder and of course it's not going to
  82. ever insert the NULL pointer so voiD0, the NULL void pointer is
  83. returned to indicate failure!
  84.  
  85. Every binder can be treated as a list by internalizing the concept
  86. of some node being the current node.  When a binder is first
  87. created no node is current.  The stack, queue, deque, and array
  88. primitives don't affect the current node index, after all it is a
  89. list concept!  Instead the primitives ins(), del(), insSort(),
  90. next(), and prev(), etc. are the list primitives and they do affect
  91. the current node pointer.  The next() primitive advances the
  92. current index and returns the void pointer to that new current node
  93. if there is one.  I'm treating the returned pointer from next() in
  94. the example as a boolean value to test when I reach the end of the
  95. list.  One of the beauties (and pains) of C++ is that of overloaded
  96. operators.  The implicit type cast operator voiD simply returns the
  97. pointer to the current node.  You will soon notice that I have a
  98. habit of capitalizing the last letter of type names to indicate a
  99. pointer to that type.  Here voiD means a pointer to void.
  100.  
  101. Okay, enough hand holding, it's time to speed up!  The next binder,
  102. B2, is constructed so that it is limited to a maximum of 3 nodes. 
  103. Upon destruction of the binder all nodes will be deleted which is
  104. indicated by setting the flags parameter to BDR_OK_FREE.  The
  105. default parameter is BDR_NO_FREE which mean that nodes are simply
  106. released when the binder is destructed.  Furthermore the functions
  107. that attempt to delete nodes are inhibited, e.g. atFree().
  108. Notice the forEach() iterator and how it applies the block
  109. parameter to each element in the binder in sequential order.  Block
  110. is SmallTalk terminology for a function passed as a parameter
  111. (C/C++ programmers say function pointers).
  112.  
  113. The last binder of this example, B3, demonstrates the other
  114. constructor which takes a vector (array) of pointers and explodes
  115. them into a binder.  By explode, I mean each cell of the vector
  116. becomes a node in the binder.  Of course the original vector is
  117. left intact.  You can implode a binder back into a dynamically
  118. allocated vector which is returned by the vector() primitive.  The
  119. overloaded ++ operator simply applies the next() primitive to the
  120. binder.  Find the definition of operator++() in binder.hpp.
  121.  
  122.  
  123.                            BDRDEM2.CPP
  124.  
  125. In this second example, you will see several of the various stack,
  126. queue, list, array, and sort primitives in action.  This is only a
  127. sampling, see the class declaration of Binder in binder.hpp for a
  128. complete listing of primitives.  If you grasp the basic structure
  129. and operation of the binder here you can fill in the details from
  130. reading the header file.
  131.  
  132.      (see bdrdem2.cpp)  omitted here to reduce download size
  133.  
  134. With the first binder, B, of this example we see the primitive
  135. atIns() used.  A binder's cells are indexed from 0 to n - 1 just
  136. like a conventional C array of n cells.  AtIns() is not a list
  137. primitive, per say, and thus doesn't affect the binder's current
  138. node index.  When no node is current, the current node number is
  139. internally set to "nodes", one position pass the last element in
  140. the binder.  Primitives that search for a node returning its index
  141. will return BDR_NOTFOUND to indicate no node found.  What may seem 
  142. strange to you at first is that BDR_NOTFOUND is a large positive
  143. integer, the largest number of nodes that can ever be stored in a
  144. binder in fact.  Remember that one less than this number will be
  145. the last element's index in a completely filled binder.  
  146.  
  147. The second binder, B2, is filled with a sorted list of the nodes
  148. from binder B.  Now both B and B2 contain pointers to the same
  149. nodes!  This could be a potentially hazardous situation but since
  150. both binders don't delete their nodes upon destruction (flags =
  151. BDR_NO_FREE in constructor) nothing is accidently deleted twice
  152. or even once since nothing was allocated to begin with.
  153.  
  154.  
  155.                           SBDRDEM1.CPP
  156.  
  157. SBinder, derived from Binder and Streamable, is streamable or
  158. persistent in OOP's parlance.  In other words, an SBinder can be
  159. saved on a stream and reloaded later.  Streamable nodes have
  160. provisions for recording double ownerships and automatically
  161. safeguarding against accidental double deletions at the same time
  162. as providing streamability as we will see in this example.
  163.  
  164. A class must include the STREAMABLE macro in its public section and
  165. be derived either publicly or privately from Streamable but never
  166. virtually or otherwise multiply inherited in order for it to be
  167. "streamable" (see StreamableInt below).  If Streamable were allowed
  168. to be a virtual base class its pointer couldn't be type casted to
  169. its derived classes when reloading.  All that is known at reload
  170. time is that it's Streamable and you get back a Streamable class
  171. instance pointer.  The ID() member function must then be called to
  172. find out what type of class was reloaded.  On the other hand if
  173. Streamable were allowed to be multiply inherited the overloaded
  174. stream operators couldn't decide, when storing on a stream, in
  175. which stream base is the valid id.  You can elect not to use the
  176. STREAMABLE macro once you understand what is required of a class in
  177. a streamable hierarchy.  I didn't use the STREAMABLE macro in
  178. declaring SBinder or CBinder but I did for SData.
  179.  
  180. Be sure you remember to call ClassName::registerClass() before
  181. attempting to store a instance of a class onto a stream!  The
  182. STREAMABLE macro declares two functions: store() and load() which
  183. you must define for your derived class.
  184.  
  185. To run this example and see the results, redirect its output to a
  186. file and than view it.  I've turned on debugging and the output
  187. will zip by on the screen otherwise.
  188.  
  189.      (see sbdrdem1.cpp)  omitted here to reduce download size
  190.  
  191. When an object X is referenced by more than one other objects that
  192. are being stored onto a stream, the first object that attempts to
  193. store X will succeed while the rest will automatically store only
  194. a link to X.  When reloading X, a link to X will be automatically
  195. held in a holding pen inside of the StreamableClassRegistry if
  196. multiple attempts had been made storing X.  When the additional
  197. objects referencing X are reloaded, the links to X are recovered. 
  198. When the last reference to X is loaded and the link is
  199. reconstructed then X is released automatically from the holding
  200. pen.  This mechanism prevents multiple copies of X from being
  201. stored on the stream and thus multiple copies reloaded.  Before
  202. attempting to store a group of objects again, perhaps to another
  203. stream, you must call restream() for each object to reset this
  204. automatic linking mechanism (the SBinder automatically calls each
  205. node's restream()).  To clear the holding pen between loads from
  206. the same stream or even different streams, you must call
  207. RestreamRegistry()!
  208.  
  209. A lot of important points are made in this example, so study it
  210. carefully!  Notice that function pointers can also be streamed when
  211. registered with the StreamableFncPtrRegistry!  And of course the
  212. ID's for the classes you derive must be unique.  Please note that
  213. 1, 2, and 3 are reserved for the SBinder, SData, and CBinder
  214. classes respectively.
  215.  
  216. When studying this example's output notice that the binder's
  217. internal compare function pointer is stored on the stream and
  218. recovered and used to sort the reloaded binder.  You can trace the
  219. multiple linking of the streamable integers.  When the binders are
  220. being destructed, the nodes are unlinked and only deleted when the
  221. refCount is zero.
  222.  
  223.  
  224.                           SBDRDEM2.CPP
  225.                
  226. An example of streamable strings can be found in sbdrdem2.cpp. 
  227. Since sbdrdem2.cpp is so much like sbdrdem1.cpp except for handling
  228. strings instead of integers, its listing has be omitted here.  You
  229. should take a look at it, however, to reenforce you understanding
  230. of deriving a class from Streamable for variant data.
  231.  
  232.  
  233.                            SDDEM1.CPP
  234.  
  235. As you can see it takes a bit of work to make an item streamable so
  236. for non class objects, the SData class has been provided for your
  237. convenience.  The next example shows how to use the SData class to
  238. wrap data at run time in a truly streamable package, complete with
  239. automatic protection against double deletion saving you the effort
  240. of deriving your own classes from Streamable.  SData allows fully
  241. heterogeneous data to be bound together in the same binder.
  242.  
  243. Notice in sddem1.cpp, that both integers and strings are bound
  244. together in the same binder!  The display and sort compare
  245. functions are coded to act on both integers and strings!  A more
  246. elegant approach would be to derive a class from SData that knows
  247. how to printOn() a stream and/or showIt() self on the console and
  248. perform some type of compare function.
  249.  
  250.      (see sddem1.cpp)  omitted here to reduce download size
  251.  
  252. The binders in sddem1.cpp are able to bind truly heterogeneous data
  253. and stream it!  The test of multilinking is also a test of
  254. protecting against double deletion.  The string "list programming!"
  255. isn't deleted twice in either the original binder or the binder
  256. loaded from the stream.
  257.  
  258.  
  259.                            CBINDER.CPP
  260.                
  261. If the data you want bound in a streamable binder is homogeneous
  262. then the CBinder can improve efficiency over using SData with the
  263. SBinder.  The CBinder can automatically handle fixed sized data
  264. such as structures and homogeneous variant data such as C strings
  265. which it defaults to, providing for both fixed and variant data the
  266. necessary packaging for streamability.  Data can be cloned and
  267. copied on the fly as well but all nodes MUST be dynamically
  268. allocated!  Keep in mind that CBinder nodes don't have the double
  269. ownership protection built-in that a true class derived from
  270. Streamable has but it doesn't require the associated coding effort
  271. either!  It's also possible to derive new classes from CBinder for
  272. handling additional sorts of homogeneous variant data by overriding
  273. the Dstore(), Dload(), Dfree(), Dclone() and Dcopy() virtual
  274. functions.
  275.  
  276. By the way, CBinder is derived from the SBinder which in turn was
  277. derived from Binder and Streamable.  Studying the cbinder.cpp
  278. source code is another excellent way to learn about building your
  279. own streamable classes.  If you do, you'll notice that
  280. CBinder::store() only has to worry about storing the additional
  281. fields of the CBinder, i.e. sizeofData, and relies on a call to
  282. SBinder::store() to store the SBinder's fields.  Likewise
  283. CBinder::load() calls SBinder::load() to load the SBinder's fields. 
  284. Writing your own load function is the more difficult of the two. 
  285. Your load() should be written in such a way as to allow it to be
  286. called from a derived class' load(), hence the test of the InstancE
  287. pointer and the subsequent call of the constructor if it's NULL
  288. (see CBinder::load() in cbinder.cpp).  You may be wondering where
  289. this constructor was declared; in the STREAMABLE macro of course
  290. (see sbinder.hpp)!  It has a dummy first parameter with no default
  291. to insure that it is a unique, unambiguous constructor from any you
  292. might declare for your newly derived class.  Notice too that
  293. initialization is left to a construct() function which can be
  294. called by either a regular constructor or
  295. YourStreamableClass::load() to initialize only the fields the
  296. load() is responsible for.  The load function has to be a static
  297. member function since it is not permissible in C++ to take the
  298. address of a constructor and a function pointer had to be recorded
  299. in the StreamableClassRegistry which could be call to reconstruct
  300. an instance when reloading.  Hence the load() of the class being
  301. loaded calls the default constructor defined in the STREAMABLE
  302. macro (how else can virtual function tables and the like be
  303. initialized in a port way?).  The construct() is called to
  304. initialize only the fields of the derived class while loading the
  305. rest of the base class members is accomplished by calling the base
  306. class' load().  You may call the base's load() either before or
  307. after extracting data from the stream but obviously your store()
  308. function must have likewise stored the data in the same fashion.
  309.  
  310.                           CBDRDEM1.CPP
  311.  
  312. And now for a CBinder demo of fixed sized nodes.  Remember a
  313. CBinder inherits all the member functions of the SBinder and that
  314. nodes must be dynamically allocated since they are deleted when the
  315. CBinder is destructed (CBinder constructor calls SBinder
  316. constructor with flags set to BDR_OK_FREE).  Since the cloned nodes
  317. of the CBinder aren't instances of a class derived from Streamable,
  318. automatic protection against double deletion of doubly owned nodes
  319. can't be provided as is the case with nodes derived from
  320. Streamable.  Moral: don't put statically allocated data into a
  321. CBinder via the inherited SBinder member functions.  Likewise don't
  322. put the same node into a CBinder more than once via the SBinder
  323. functions.  If you do, then when the CBinder goes to destruct
  324. itself or if you call allFree() or atFree(), etc. it's possible to
  325. either attempt to delete a statically allocated node or some node
  326. that has already been deleted - look out (crash)!  You might wonder
  327. why the SBinder functions are left exposed (publicly inherited) -
  328. because you algorithm may require must nodes between CBinder such
  329. as with queuing network problems or other simulations.
  330.  
  331. You'll note in the example that new primitives have be added by the
  332. CBinder class, e.g. pushCLN(), which do the same thing that their
  333. SBinder counterparts do only cloning or copying the data as
  334. required automatically but otherwise performing the same operation. 
  335. You could do the same thing yourself without a CBinder but you
  336. would have to clone and/or copy everything yourself.  If you are
  337. porting from FlexList, you'll see that the CopyBinder rounds out
  338. the SBinder to encompass the full "by value" operational capability
  339. of a FlexList.
  340.  
  341. This demo shows that the CBinder provides for synthetic streamable
  342. nodes without your having to derive a new class from Streamable. 
  343. You must still register the CBinder with the stream registry, and
  344. function pointers as usual, as well as calling restream() but you
  345. are saved from rewriting store() and load() functions for a
  346. packaging class derived from streamable.
  347.  
  348.      (see cbdrdem1.cpp)  omitted here to reduce download size
  349.  
  350.  
  351.                           CBDRDEM2.CPP
  352.                
  353. Be sure to look at cbdrdem2.cpp to see how the CBinder defaults to
  354. handling strings so you never have to write something like
  355. sbdrdem2.cpp yourself.
  356.  
  357.  
  358.                              SUMMARY
  359.  
  360. As you become more familiar with the LDB family you will realize
  361. that you can derive your own LDB classes from Binder, SBinder,
  362. SData, and CBinder.  For example, SBinder along with SData can be
  363. use as a basis for an adjacency list binder, or sparse matrix,
  364. tree, or graph network because of the strategic virtualizing of
  365. member functions such as Dfree(), Dstore(), Dload() and the back-
  366. link capability of the Streamable class.  Note also that you can
  367. derive LDB's from the CBinder to handle what would otherwise be
  368. non-streamable classes and structures thanks to the virtualization
  369. of such functions as Dclone() and Dcopy() and Dfree().  The
  370. possibilities are enormous.
  371.  
  372.